#pragma once

#include <iostream>
using namespace std;
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "task.hpp"

int DefaultNum = 5;

template <class T>
class ThreadPool
{
public:
    static ThreadPool<T> *GetInstance()
    {
        static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
        // 当前单例对象还未创建
        if (ins == nullptr) // 双判定，减少后续锁的争用，提高获取单例效率
        {
            // 除了第一次创建需要争用锁，后续不需要锁，所以使用双判定
            pthread_mutex_lock(&lock);
            if (ins == nullptr)
            {
                ins = new ThreadPool<T>();
                ins->InitThreadPool();
                cout << "首次加载对象" << endl;
            }
            pthread_mutex_unlock(&lock);
        }
        return ins;
    }

    void PushTask(const T &in)
    {
        // 派发任务时应该加锁，防止线程拿任务
        Lock();
        _task_queue.push(in);
        Unlock();
        // 派发完任务一定要唤醒线程，否则线程将一直休眠
        WakeUp();
    }

private:
    // 由于线程执行方法参数只能有一个 void* ，而成员函数参数都有this指针
    // 所以线程执行方法应该是static的
    static void *Routine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadPool<T> *tp = (ThreadPool<T> *)args;
        while (true)
        {
            tp->Lock();
            // 使用while循环判断，确保是任务队列有任务才被唤醒，避免伪唤醒和挂起失败情况
            while (tp->Empty())
            {
                // 挂起等待时会自动释放锁，唤醒后会自动竞争锁
                tp->Wait();
            }
            T task;
            tp->PopTask(&task);
            // 拿到任务就可以释放锁了，不用拿着锁处理任务
            tp->Unlock();
            task.run();
        }
    }

    void InitThreadPool()
    {
        pthread_t tid;
        for (int i = 0; i < _num; i++)
            pthread_create(&tid, nullptr, Routine, (void *)this);
        // 因为线程执行方法中需要执行成员函数，所以传递this指针给Routine
    }

    void PopTask(T *out)
    {
        *out = _task_queue.front();
        _task_queue.pop();
    }

    void Lock()
    {
        pthread_mutex_lock(&mtx);
    }

    void Unlock()
    {
        pthread_mutex_unlock(&mtx);
    }

    void Wait()
    {
        pthread_cond_wait(&_cond, &mtx);
    }

    void WakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    bool Empty()
    {
        return _task_queue.empty();
    }

private:
    // 构造函数必须要有，但是必须私有化
    ThreadPool(int num = DefaultNum)
        : _num(num)
    {
        pthread_mutex_init(&mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    // 不允许拷贝构造
    ThreadPool(const ThreadPool<T> &tp) = delete;
    ThreadPool<T> &operator=(ThreadPool<T> tp) = delete;

    ~ThreadPool()
    {
        pthread_mutex_destroy(&mtx);
        pthread_cond_destroy(&_cond);
    }

private:
    int _num; // 线程池中线程数量
    queue<T> _task_queue;
    pthread_mutex_t mtx;
    pthread_cond_t _cond;

    static ThreadPool<T> *ins;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::ins = nullptr;